From a64b9073f483ffc27b777708d495d718b4bb9f81 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 13 Jul 2014 11:47:37 -0700 Subject: [PATCH] Add a `cargo run` command This currently only supports executing the `src/main.rs` convention, no other. Close #149 --- Makefile | 1 + src/bin/cargo-build.rs | 4 +- src/bin/cargo-run.rs | 69 ++++++++++++++++++++++++++++ src/bin/cargo-test.rs | 5 +- src/bin/cargo.rs | 6 ++- src/cargo/ops/cargo_compile.rs | 9 ++-- src/cargo/ops/cargo_run.rs | 23 ++++++++++ src/cargo/ops/mod.rs | 2 + tests/test_cargo_run.rs | 84 ++++++++++++++++++++++++++++++++++ tests/tests.rs | 1 + 10 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 src/bin/cargo-run.rs create mode 100644 src/cargo/ops/cargo_run.rs create mode 100644 tests/test_cargo_run.rs diff --git a/Makefile b/Makefile index e10fedb1a..fa107cddf 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ BINS = cargo \ cargo-verify-project \ cargo-git-checkout \ cargo-test \ + cargo-run SRC = $(shell find src -name '*.rs' -not -path 'src/bin*') diff --git a/src/bin/cargo-build.rs b/src/bin/cargo-build.rs index 9e4ef547e..81c510212 100644 --- a/src/bin/cargo-build.rs +++ b/src/bin/cargo-build.rs @@ -56,7 +56,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { "compile" }; - let opts = CompileOptions { + let mut opts = CompileOptions { update: options.update_remotes, env: env, shell: shell, @@ -64,7 +64,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { target: options.target.as_ref().map(|t| t.as_slice()), }; - ops::compile(&root, opts).map(|_| None).map_err(|err| { + ops::compile(&root, &mut opts).map(|_| None).map_err(|err| { CliError::from_boxed(err, 101) }) } diff --git a/src/bin/cargo-run.rs b/src/bin/cargo-run.rs new file mode 100644 index 000000000..216d783da --- /dev/null +++ b/src/bin/cargo-run.rs @@ -0,0 +1,69 @@ +#![crate_name = "cargo-run"] +#![feature(phase)] + +#[phase(plugin, link)] +extern crate cargo; +extern crate serialize; + +#[phase(plugin, link)] +extern crate hammer; + +use std::os; +use std::io::process::ExitStatus; + +use cargo::ops; +use cargo::{execute_main_without_stdin}; +use cargo::core::{MultiShell}; +use cargo::util::{CliResult, CliError}; +use cargo::util::important_paths::find_project_manifest; + +#[deriving(PartialEq,Clone,Decodable)] +struct Options { + manifest_path: Option, + jobs: Option, + update: bool, + rest: Vec, +} + +hammer_config!(Options "Run the package's main executable", |c| { + c.short("jobs", 'j').short("update", 'u') +}) + +fn main() { + execute_main_without_stdin(execute); +} + +fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { + let root = match options.manifest_path { + Some(path) => Path::new(path), + None => try!(find_project_manifest(&os::getcwd(), "Cargo.toml") + .map_err(|_| { + CliError::new("Could not find Cargo.toml in this \ + directory or any parent directory", + 102) + })) + }; + + let mut compile_opts = ops::CompileOptions { + update: options.update, + env: "compile", + shell: shell, + jobs: options.jobs, + target: None, + }; + + let err = try!(ops::run(&root, &mut compile_opts, + options.rest.as_slice()).map_err(|err| { + CliError::from_boxed(err, 101) + })); + match err { + None => Ok(None), + Some(err) => { + Err(match err.exit { + Some(ExitStatus(i)) => CliError::from_boxed(box err, i as uint), + _ => CliError::from_boxed(box err, 101), + }) + } + } +} + diff --git a/src/bin/cargo-test.rs b/src/bin/cargo-test.rs index fdd9dce72..eb9dfeaf9 100644 --- a/src/bin/cargo-test.rs +++ b/src/bin/cargo-test.rs @@ -44,7 +44,7 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { })) }; - let compile_opts = ops::CompileOptions { + let mut compile_opts = ops::CompileOptions { update: options.update, env: "test", shell: shell, @@ -52,7 +52,8 @@ fn execute(options: Options, shell: &mut MultiShell) -> CliResult> { target: None, }; - let test_executables = try!(ops::compile(&root, compile_opts).map_err(|err| { + let test_executables = try!(ops::compile(&root, + &mut compile_opts).map_err(|err| { CliError::from_boxed(err, 101) })); diff --git a/src/bin/cargo.rs b/src/bin/cargo.rs index 8379a85eb..2de801900 100644 --- a/src/bin/cargo.rs +++ b/src/bin/cargo.rs @@ -51,8 +51,10 @@ fn execute() { println!("Commands:"); println!(" build # compile the current project"); println!(" test # run the tests"); - println!(" clean # remove the target directory\n"); - + println!(" clean # remove the target directory"); + println!(" run # build and execute src/main.rs"); + println!(""); + let (_, options) = hammer::usage::(false); println!("Options (for all commands):\n\n{}", options); diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 9fdf73b20..18b0c0587 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -38,8 +38,9 @@ pub struct CompileOptions<'a> { pub target: Option<&'a str>, } -pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult> { - let CompileOptions { update, env, shell, jobs, target } = options; +pub fn compile(manifest_path: &Path, + options: &mut CompileOptions) -> CargoResult> { + let CompileOptions { update, env, ref mut shell, jobs, target } = *options; let target = target.map(|s| s.to_string()); log!(4, "compile; manifest-path={}", manifest_path.display()); @@ -60,7 +61,7 @@ pub fn compile(manifest_path: &Path, options: CompileOptions) -> CargoResult CargoResult>(); - let mut config = try!(Config::new(shell, update, jobs, target)); + let mut config = try!(Config::new(*shell, update, jobs, target)); try!(ops::compile_targets(env.as_slice(), targets.as_slice(), &package, &PackageSet::new(packages.as_slice()), &resolve, &mut config)); diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs new file mode 100644 index 000000000..6b6198a0d --- /dev/null +++ b/src/cargo/ops/cargo_run.rs @@ -0,0 +1,23 @@ +use std::os; + +use ops; +use util::{CargoResult, human, process, ProcessError}; + +pub fn run(manifest_path: &Path, + options: &mut ops::CompileOptions, + args: &[String]) -> CargoResult> { + if !manifest_path.dir_path().join("src").join("main.rs").exists() { + return Err(human("`src/main.rs` must be present for `cargo run`")) + } + + try!(ops::compile(manifest_path, options)); + let exe = manifest_path.dir_path().join("target/main"); + let exe = match exe.path_relative_from(&os::getcwd()) { + Some(path) => path, + None => exe, + }; + let process = process(exe).args(args); + + try!(options.shell.status("Running", process.to_string())); + Ok(process.exec().err()) +} diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index d47b13620..cba0e894a 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -2,8 +2,10 @@ pub use self::cargo_clean::clean; pub use self::cargo_compile::{compile, CompileOptions}; pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages}; pub use self::cargo_rustc::compile_targets; +pub use self::cargo_run::run; mod cargo_clean; mod cargo_compile; mod cargo_read_manifest; mod cargo_rustc; +mod cargo_run; diff --git a/tests/test_cargo_run.rs b/tests/test_cargo_run.rs new file mode 100644 index 000000000..355884e44 --- /dev/null +++ b/tests/test_cargo_run.rs @@ -0,0 +1,84 @@ +use std::path; + +use support::{project, execs}; +use support::{COMPILING, RUNNING}; +use hamcrest::{assert_that, existing_file}; + +fn setup() { +} + +test!(simple { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() { println!("hello"); } + "#); + + assert_that(p.cargo_process("cargo-run"), + execs().with_status(0).with_stdout(format!("\ +{compiling} foo v0.0.1 (file:{dir}) +{running} `target{sep}main` +hello +", + compiling = COMPILING, + running = RUNNING, + dir = p.root().display(), + sep = path::SEP).as_slice())); + assert_that(&p.bin("main"), existing_file()); +}) + +test!(simple_with_args { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() { + assert_eq!(std::os::args().get(1).as_slice(), "hello"); + assert_eq!(std::os::args().get(2).as_slice(), "world"); + } + "#); + + assert_that(p.cargo_process("cargo-run").arg("hello").arg("world"), + execs().with_status(0)); +}) + +test!(exit_code { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() { std::os::set_exit_status(2); } + "#); + + assert_that(p.cargo_process("cargo-run"), + execs().with_status(2)); +}) + +test!(no_main_file { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", ""); + + assert_that(p.cargo_process("cargo-run"), + execs().with_status(101) + .with_stderr("`src/main.rs` must be present for \ + `cargo run`\n")); +}) diff --git a/tests/tests.rs b/tests/tests.rs index 595287a18..7db578ec7 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -27,3 +27,4 @@ mod test_cargo_compile_path_deps; mod test_cargo_test; mod test_shell; mod test_cargo_cross_compile; +mod test_cargo_run; -- 2.30.2